#include <g-bios.h>
#include <flash/data_flash.h>
#include <flash/flash.h>
#include <core/bitops.h>
#include <arch/at91sam926x.h>



static struct PartAttr g_defDataFlashParts[] =
{
	{
		PT_BL_TH,
		KB(4) / 512 * 528,
	},
	{
		PT_BL_GBIOS,
		KB(256 - 4) / 512 * 528,
	},
	{
		PT_BL_CFG,
		(MB(2) - KB(256)) / 512 * 528,
	},
	{
		PT_MBR,
	},
};


static int DataFlashSpiEnable(UINT32);

static int DataFlashSpiOps(struct Flash *);

static int DataFlashStatus(struct Flash *, BYTE *);

static int DataFlashWaitReady(struct Flash *);

static int DataFlashErase(struct Flash *, struct FerOpt *);

static int DataFlashWrite(struct Flash *, ULONG, ULONG, ULONG *, const BYTE *);

static int DataFlashRead(struct Flash *, ULONG, ULONG, ULONG *, BYTE *);

static int DataFlashAdd(const char *, UINT32, UINT32, UINT32);



static int DataFlashSpiEnable(UINT32 ulCS) 
{
	volatile struct AT9261_SPI *pSpi0 = (struct AT9261_SPI *)AT91SAM926X_PA_SPI0;

	switch (ulCS)
	{
	case 0:
		pSpi0->dwSPIMR &= 0xFFF0FFFF;
		pSpi0->dwSPIMR |= ((AT91C_SPI_PCS0_SERIAL_DATAFLASH) & AT91C_SPI_PCS);

		break;

	default:
		return -EINVAL;
	}

	pSpi0->dwSPICR = AT91C_SPI_SPIEN;

	return 0;
}


static int DataFlashSpiOps(struct Flash *pFlash)
{
	struct DataFlash *pDataflash = MemToObj(pFlash, struct DataFlash, parent);
   	UINT32 ulTimeout;
	UINT32 ulSrcAddr;
	volatile struct AT9261_SPI *pSpi0 = (struct AT9261_SPI *)AT91SAM926X_PA_SPI0;

	DPRINT("\ncmd:%02x%02x%02x%02x\n", 
			*((char *)pDataflash->stOprMsg.pRxCmdBuf),
			*((char *)pDataflash->stOprMsg.pRxCmdBuf + 1),
			*((char *)pDataflash->stOprMsg.pRxCmdBuf + 2),
			*((char *)pDataflash->stOprMsg.pRxCmdBuf + 3)
		   );
		   
	DPRINT("nRxCmdLen=%x\nnTxCmdLen=%x\nhasData=%x\npRxDataBuf=0x%08x\npTxDataBuf=0x%08x\nnRxDataLen=%x\nnTxDataLen=%x\n",
			pDataflash->stOprMsg.nRxCmdLen,
			pDataflash->stOprMsg.nTxCmdLen,
			pDataflash->stOprMsg.hasData,
			pDataflash->stOprMsg.pRxDataBuf,
			pDataflash->stOprMsg.pTxDataBuf,
			pDataflash->stOprMsg.nRxDataLen,
			pDataflash->stOprMsg.nTxDataLen
		   );

	pSpi0->dwSPIPTCR = AT91C_PDC_TXTDIS + AT91C_PDC_RXTDIS;

	pSpi0->dwSPIRPR = (UINT32)pDataflash->stOprMsg.pRxCmdBuf;
	pSpi0->dwSPITPR = (UINT32)pDataflash->stOprMsg.pTxCmdBuf;

	pSpi0->dwSPIRCR = (UINT16)pDataflash->stOprMsg.nRxCmdLen; 
	pSpi0->dwSPITCR = (UINT16)pDataflash->stOprMsg.nTxCmdLen; 

	if (TRUE == pDataflash->stOprMsg.hasData)
	{
		pSpi0->dwSPIRNPR = (UINT32)pDataflash->stOprMsg.pRxDataBuf;
		pSpi0->dwSPITNPR = (UINT32)pDataflash->stOprMsg.pTxDataBuf;

		pSpi0->dwSPIRNCR = (UINT16)pDataflash->stOprMsg.nRxDataLen;
		pSpi0->dwSPITNCR = (UINT16)pDataflash->stOprMsg.nTxDataLen;
	}

	ulTimeout = 0;
	pSpi0->dwSPIPTCR = AT91C_PDC_TXTEN + AT91C_PDC_RXTEN; 
	while(!(pSpi0->dwSPISR & AT91C_SPI_RXBUFF) && ulTimeout < CFG_SPI_WRITE_TOUT) // AT91C_SPI_TXBUFE is error!!
		ulTimeout++;

   	pSpi0->dwSPIPTCR = AT91C_PDC_TXTDIS + AT91C_PDC_RXTDIS; 

	if (ulTimeout >= CFG_SPI_WRITE_TOUT)
		return -EFAULT;

	return 0;
}


static int DataFlashStatus(struct Flash *pFlash, BYTE *pbStatus)
{
	struct DataFlash *pDataFlash = MemToObj(pFlash, struct DataFlash, parent);
	BYTE *pCmd;


	pDataFlash->stOprMsg.pRxCmdBuf = pDataFlash->stOprMsg.pTxCmdBuf = pCmd = pDataFlash->bCommand;
	pDataFlash->stOprMsg.nRxCmdLen = pDataFlash->stOprMsg.nTxCmdLen = 2;
	pDataFlash->stOprMsg.hasData   = FALSE;

	pCmd[0] = OP_READ_STATUS;
	pCmd[1] = 0;

	if (DataFlashSpiOps(pFlash) < 0)
		return -EINVAL;

	*pbStatus = *((BYTE *)pDataFlash->stOprMsg.pRxCmdBuf + 1);

	DPRINT("status:%02x\n", *pbStatus);

	return 0;
}


static int DataFlashWaitReady(struct Flash *pFlash)
{
	struct DataFlash *pDataFlash = MemToObj(pFlash, struct DataFlash, parent);
	int	ret;
	BYTE status;

	DataFlashSpiEnable(pDataFlash->ulChipSelect);

	for (;;)
	{
		ret = DataFlashStatus(pFlash, &status);
		if (ret < 0) 
		{
			DPRINT("%s: status %d?\n", 	pFlash->szName, status);
			continue;
		}

		if ((status & (1 << 7)))
			break;
	}

	return 0;
}


static int DataFlashErase(struct Flash *pFlash, struct FerOpt *pErsOp)
{
	struct DataFlash *pDataFlash = MemToObj(pFlash, struct DataFlash, parent);
	UINT32 blocksize = pDataFlash->nBlockSize, nLen = pErsOp->len;
	BYTE   *pCmd;
	UINT32 do_block = 0;

	DataFlashSpiEnable(pDataFlash->ulChipSelect);

	if ((pErsOp->addr + pErsOp->len) > pFlash->nChipSize
		|| pErsOp->len % pDataFlash->nPageSize != 0
		|| pErsOp->addr % pDataFlash->nPageSize != 0
	   )
	{
		DPRINT("ERROR: address or lenght is not correct!\n");
		return -EINVAL;
	}


	(void) DataFlashWaitReady(pFlash);

	while (pErsOp->len > 0) 
	{
		UINT32	pageaddr;
		int		status;
		int		do_block;

		pageaddr = pErsOp->addr / pDataFlash->nPageSize;
#if 0
		do_block = ((pageaddr & 0x7) == 0) && (pErsOp->len >= blocksize);
#else
		do_block = FALSE;
#endif
		pageaddr = pageaddr << pDataFlash->nPageShift;

		pDataFlash->stOprMsg.pTxCmdBuf = pDataFlash->stOprMsg.pRxCmdBuf = pCmd = pDataFlash->bCommand;
		pDataFlash->stOprMsg.nTxCmdLen = pDataFlash->stOprMsg.nRxCmdLen = 4;
		pDataFlash->stOprMsg.hasData   = FALSE;

		pCmd[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE;
		pCmd[1] = (pageaddr & 0x00FF0000) >> 16;
		pCmd[2] = (pageaddr & 0x0000FF00) >> 8;
		pCmd[3] = 0;

		status = DataFlashSpiOps(pFlash);
		(void) DataFlashWaitReady(pFlash);

		if (status < 0)
		{
			DPRINT( "%s: erase %stOpMsg, err %d\n", pFlash->szName, pageaddr, status);

			continue;
		}


		if (NULL != pFlash->pHookFunc && NULL != pFlash->pHookParam)
		{
			pFlash->pHookParam->nPageIndex = pageaddr >> pDataFlash->nPageShift;
			pFlash->pHookFunc(pFlash, pFlash->pHookParam);
		}

		if (do_block) 
		{
			pErsOp->addr += blocksize;
			pErsOp->len  -= blocksize;
		}
		else 
		{
			pErsOp->addr += pDataFlash->nPageSize;
			pErsOp->len  -= pDataFlash->nPageSize;
		}

	}

	pErsOp->state = FLASH_ERASE_DONE;

	return 0;
}


static int DataFlashWrite(struct Flash *pFlash, ULONG to, ULONG len,
							   ULONG *retlen, const BYTE *buf)
{
	struct DataFlash *pDataFlash = MemToObj(pFlash, struct DataFlash, parent);
	UINT32 pageaddr, addr, offset, writelen;
	UINT32 remaining = len;
	BYTE *writebuf = (BYTE *)buf;
	BYTE *pbCmd, *pTmpBuf;
	int	status = -EINVAL;

	DataFlashSpiEnable(pDataFlash->ulChipSelect);

	DPRINT("%s: write 0x%x..0x%x\n", pFlash->szName, (unsigned)to, (unsigned)(to + len));

	*retlen = 0;

	if (0 == len)
		return 0;
	if ((to + len) > pFlash->nChipSize)
		return -EINVAL;

	pTmpBuf = malloc(pDataFlash->nPageSize);
	if (NULL == pTmpBuf)
	{
		DPRINT("%s, %d\n", __FUNCTION__, __LINE__);
		return -ENOMEM;
	}

	pageaddr = to / pDataFlash->nPageSize;
	offset   = to % pDataFlash->nPageSize;
	if (offset + len > pDataFlash->nPageSize)
		writelen = pDataFlash->nPageSize - offset;
	else
		writelen = len;

	(void) DataFlashWaitReady(pFlash);

	while (remaining > 0) 
	{
		DPRINT("write @ %x:%x len=%x\n", pageaddr, offset, writelen);

		addr = pageaddr << pDataFlash->nPageShift;

		if (writelen != pDataFlash->nPageSize) 
		{
			pDataFlash->stOprMsg.pTxCmdBuf = pDataFlash->stOprMsg.pRxCmdBuf = pbCmd = pDataFlash->bCommand;
			pDataFlash->stOprMsg.nTxCmdLen = pDataFlash->stOprMsg.nRxCmdLen = 4;
			pDataFlash->stOprMsg.hasData   = FALSE;
			pbCmd[0] = OP_TRANSFER_BUF1;
			pbCmd[1] = (addr & 0x00FF0000) >> 16;
			pbCmd[2] = (addr & 0x0000FF00) >> 8;
			pbCmd[3] = 0;


	
			DPRINT("TRANSFER: (%x) %x %x %x\n",	pbCmd[0], pbCmd[1], pbCmd[2], pbCmd[3]);

			status = DataFlashSpiOps(pFlash);
			if (status < 0)
				DPRINT("%s: xfer %u -> %d \n", pFlash->szName, addr, status);

			(void) DataFlashWaitReady(pFlash);
		}

		addr += offset;
		pDataFlash->stOprMsg.pTxCmdBuf  = pDataFlash->stOprMsg.pRxCmdBuf = pbCmd = pDataFlash->bCommand;
		pDataFlash->stOprMsg.nTxCmdLen  = pDataFlash->stOprMsg.nRxCmdLen = 4;
		pDataFlash->stOprMsg.hasData    = TRUE;
		//pDataFlash->stOprMsg.pTxDataBuf = pDataFlash->stOprMsg.pRxDataBuf = writebuf;
		pDataFlash->stOprMsg.pTxDataBuf = writebuf;
		pDataFlash->stOprMsg.pRxDataBuf = pTmpBuf;
		pDataFlash->stOprMsg.nTxDataLen = pDataFlash->stOprMsg.nRxDataLen = writelen;
		pbCmd[0] = OP_PROGRAM_VIA_BUF1;
		pbCmd[1] = (addr & 0x00FF0000) >> 16;
		pbCmd[2] = (addr & 0x0000FF00) >> 8;
		pbCmd[3] = (addr & 0x000000FF);

		DPRINT("PROGRAM: (%x) %x %x %x, len:%x\n", pbCmd[0], pbCmd[1], pbCmd[2], pbCmd[3], writelen);

		status = DataFlashSpiOps(pFlash);

		if (status < 0)
			DPRINT("%s: pgm %u/%u -> %d \n", pFlash->szName, addr, writelen, status);

		(void) DataFlashWaitReady(pFlash);

		if (NULL != pFlash->pHookFunc && NULL != pFlash->pHookParam)
		{
			pFlash->pHookParam->nPageIndex = pageaddr;
			pFlash->pHookFunc(pFlash, pFlash->pHookParam);
		}


#ifdef CONF_DATAFLASH_WRITE_VERIFY // test ok

		
		addr = pageaddr << pDataFlash->nPageShift;
		pDataFlash->stOprMsg.pTxCmdBuf = pDataFlash->stOprMsg.pRxCmdBuf = pbCmd = pDataFlash->bCommand;
		pDataFlash->stOprMsg.nTxCmdLen = pDataFlash->stOprMsg.nRxCmdLen = 4;
		pDataFlash->stOprMsg.hasData   = FALSE;
		pbCmd[0] = OP_COMPARE_BUF1;
		pbCmd[1] = (addr & 0x00FF0000) >> 16;
		pbCmd[2] = (addr & 0x0000FF00) >> 8;
		pbCmd[3] = 0;

		DPRINT("COMPARE: (%x) %x %x %x\n", pbCmd[0], pbCmd[1], pbCmd[2], pbCmd[3]);

		status = DataFlashSpiOps(pFlash);
		if (status < 0)
			DPRINT("%s: compare %u -> %d \n", pFlash->szName, addr, status);

		status = DataFlashWaitReady(pFlash);

		
		if ((status & (1 << 6)) == 1) 
		{
			DPRINT("%s: compare page %u, err %d\n",	pFlash->szName, pageaddr, status);
			remaining = 0;
			status = -EIO;
			break;
		}
		else
		{
			status = 0;
		}
#endif	

		remaining = remaining - writelen;
		offset    = 0;
		writebuf += writelen;
		*retlen  += writelen;
		pageaddr++;

		if (remaining > pDataFlash->nPageSize)
			writelen = pDataFlash->nPageSize;
		else
			writelen = remaining;


	}

	free(pTmpBuf);

	return status;
}


static int DataFlashRead(struct Flash *pFlash, ULONG from, ULONG len,
			ULONG *retlen, BYTE *buf)
{
	struct DataFlash *pDataFlash = MemToObj(pFlash, struct DataFlash, parent);
	UINT32 addr, nPage ,nRemainLen, nReadLen;
	BYTE *pbCmd, *pbBuffer;
	int	status;
	UINT32 isBlock;

	DataFlashSpiEnable(pDataFlash->ulChipSelect);

	DPRINT("%s: read 0x%x..0x%x\n", pFlash->szName, from, (from + len));

	*retlen = 0;

	if (0 == len)
		return 0;
	if (from + len > pFlash->nChipSize)
		return -EINVAL;

	nPage = from / pDataFlash->nPageSize;
	addr  = (nPage << pDataFlash->nPageShift) + (from % pDataFlash->nPageSize);

	nRemainLen = len;
	pbBuffer   = buf;

	(void) DataFlashWaitReady(pFlash);

	// spi tx/rx count is 16-bit registers,  so need to split the length, here choose pagesize as basic unit
	while (nRemainLen > 0)
	{
#if 0
		if (nRemainLen >= pDataFlash->nBlockSize)
			nReadLen = pDataFlash->nBlockSize;
		else if (nRemainLen >= pDataFlash->nPageSize)
			nReadLen = pDataFlash->nPageSize;
		else
			nReadLen = nRemainLen;
#else
		if (nRemainLen >= pDataFlash->nPageSize)
			nReadLen = pDataFlash->nPageSize;
		else
			nReadLen = nRemainLen;
#endif

		pDataFlash->stOprMsg.pTxCmdBuf  = pDataFlash->stOprMsg.pRxCmdBuf = pbCmd = pDataFlash->bCommand;
		pDataFlash->stOprMsg.nTxCmdLen  = pDataFlash->stOprMsg.nRxCmdLen = 8;
		pDataFlash->stOprMsg.hasData    = TRUE;
		pDataFlash->stOprMsg.pRxDataBuf = pDataFlash->stOprMsg.pTxDataBuf = pbBuffer;
		pDataFlash->stOprMsg.nRxDataLen = pDataFlash->stOprMsg.nTxDataLen = nReadLen;

		pbCmd[0] = OP_READ_CONTINUOUS;
		pbCmd[1] = (addr & 0x00FF0000) >> 16;
		pbCmd[2] = (addr & 0x0000FF00) >> 8;
		pbCmd[3] = (addr & 0x000000FF);

		status = DataFlashSpiOps(pFlash);
		(void) DataFlashWaitReady(pFlash);

		if (status >= 0)
		{
			(*retlen) += nReadLen;
			status = 0;
		} 
		else
		{
			DPRINT("%s: read %x..%x --> %d\n", pFlash->szName, (unsigned)from, (unsigned)(from + len), status);
		}

		if (NULL != pFlash->pHookFunc && NULL != pFlash->pHookParam)
		{
			pFlash->pHookParam->nPageIndex = nPage;
			pFlash->pHookFunc(pFlash, pFlash->pHookParam);
		}

		pbBuffer   += nReadLen;
		nRemainLen -= nReadLen;
		if (pDataFlash->nPageSize == nReadLen)
			nPage  += 1;
		else if (pDataFlash->nBlockSize == nReadLen)
			nPage  += 8;

		addr = nPage << pDataFlash->nPageShift;


	}

	return status;
}


static int DataFlashReadOOB(struct Flash *pFlash, ULONG ulLen, struct OobOpt *pOps)
{
	pFlash = pFlash;
	ulLen  = ulLen;
	pOps   = pOps;

	return 0;
}


static int DataFlashWriteOOB(struct Flash *pFlash, ULONG ulLen, struct OobOpt *pOps)
{
	pFlash = pFlash;
	ulLen  = ulLen;
	pOps   = pOps;

	return 0;
}


static int DataFlashIsBad(struct Flash *pFlash, ULONG nAddr)
{
	pFlash = pFlash;
	nAddr  = nAddr;

	return 0;
}


static int DataFlashMarkBad(struct Flash *pFlash, ULONG nAddr)
{
	pFlash = pFlash;
	nAddr  = nAddr;

	return 0;
}


static int DataFlashAdd(const char *pszName, UINT32 nPages, UINT32 nPageSize, UINT32 nPageOffset)
{
	struct DataFlash *pDataFlash;
	struct Flash	 *pFlash;
	UINT32 ulRet;
	struct PartInfo *pPartInfo;
	ULONG nMbrLen;

	pDataFlash = malloc(sizeof(struct DataFlash));
	if (NULL == pDataFlash)
		return -ENOMEM;


	//pDataFlash->nPageSize   = 1 << (nPageOffset - 1);
	pDataFlash->nPageSize    = nPageSize;
	pDataFlash->nPageShift   = nPageOffset;
	pDataFlash->nBlockSize   = pDataFlash->nPageSize << 3; // fixme: whether 8 is also correct for other dataflash
	pDataFlash->nBlockShift  = pDataFlash->nPageShift + 3;
	pDataFlash->ulChipSelect = 0;

	pFlash = &pDataFlash->parent;
	strcpy(pDataFlash->szName, pszName);
	strcpy(pFlash->szName, pDataFlash->szName);	// fixme : how to name this flash

	pFlash->nType        = FLASH_DATAFLASH;

	//pFlash->nChipSize    = nPages * pDataFlash->nPageSize;
	pFlash->nChipSize    = nPages * nPageSize;
	pFlash->nEraseSize   = pDataFlash->nPageSize;
	pFlash->nWriteSize   = pDataFlash->nPageSize;
	pFlash->nEraseShift  = pDataFlash->nPageShift;
	pFlash->nWriteShift  = pDataFlash->nPageShift;
	pFlash->nOobSize     = 0;
//	nPartNum     = 0;

	pFlash->Read         = DataFlashRead;
	pFlash->Write        = DataFlashWrite;
	pFlash->Erase        = DataFlashErase;

	pFlash->isBadAllow   = FALSE;
	pFlash->ReadOob 	 = DataFlashReadOOB;
	pFlash->WriteOob 	 = DataFlashWriteOOB;
	pFlash->BlockIsBad 	 = DataFlashIsBad;
	pFlash->BlockMarkBad = DataFlashMarkBad;

	pFlash->pHookFunc    = NULL;
	pFlash->pHookParam   = NULL;

	DPRINT("pagesize=%d\nblocksize=%d\npageshift=%d\nblockshift=%d\nchipsize=%x\n",
			  pDataFlash->nPageSize, pDataFlash->nBlockSize,
			  pDataFlash->nPageShift, pDataFlash->nBlockShift, pFlash->nChipSize);
			  

	ulRet = GkFlashRegister(pFlash);
	if (ulRet < 0)
	{
		DPRINT("%s(): fail to register deivce %S!\n", __FUNCTION__, pFlash->szName);
		// fixme: destroy

		return ulRet;
	}

	printf("%s dataflash detected!\n", pszName);

	// fixme: partition support may change
	pFlash->nMbrAddr = MB(2) / 512 * 528;
	pFlash->nMbrSize = pFlash->nEraseSize;

	{
		nMbrLen  = pFlash->nMbrSize;

		pPartInfo = (struct PartInfo *)malloc(nMbrLen);
		BUG_IF(NULL == pPartInfo);

		memset(pPartInfo, 0, nMbrLen);

		pFlash->Read(pFlash, pFlash->nMbrAddr, nMbrLen, &nMbrLen, (BYTE *)pPartInfo);

		// fixme: add partition checking
		if (GB_MAGIC_MBR == pPartInfo->nMagic)
		{
			GuPartCreateAll(pFlash, pPartInfo->vParts, pPartInfo->nParts);
		}
		else
		{
			printf("Partitions: set to default.\n");
			GuPartCreateAll(pFlash, g_defDataFlashParts, ARRAY_ELEM_NUM(g_defDataFlashParts));
		}

		free(pPartInfo); 
	}

	return ulRet;
}


void DataFlashSpiInit(void) 
{
	volatile struct AT9261_PIO *pPio = (struct AT9261_PIO *)AT91SAM926X_PA_PIOA;
	volatile struct AT9261_PMC *pPmc = (struct AT9261_PMC *)AT91SAM926X_PA_PMC;
	volatile struct AT9261_SPI *pSpi = (struct AT9261_SPI *)AT91SAM926X_PA_SPI0;
	UINT32 ulPAEnable, ulPBEnable;

	
	ulPAEnable = 0x1 << 0 | 0x1 << 1 | 0x1 << 2 | 0x1 << 3 | 0x1 << 4 | 0x1 << 5 | 0x1 << 6;
	ulPBEnable = 0x1 << 27 | 0x1 << 28 | 0x1 << 29;

	pPio->dwASR = ulPAEnable;
	pPio->dwBSR = ulPBEnable;
	pPio->dwPDR = ulPAEnable | ulPBEnable;


	//disable pullup for PA0-PA6
	pPio->dwPPUDR = ~0xFFFFFF80;
	pPio->dwPPUER = 0xFFFFFF80;


	
	pPmc->dwPCER = 0x1 << 12;


	
	pSpi->dwSPICR = 0x1 << 7;


	
	pSpi->dwSPIMR = 0x1 << 0 | 0x1 << 4 | 0xF << 16;


	
	pSpi->dwSPICSR[0] = 0x1 << 1 | 0x0 << 4 | 0x3 << 8 | (0xFF << 16 & 0x1A << 16) | (0xFF << 24 & 0x1 << 24);


	pSpi->dwSPICR = AT91C_SPI_SPIEN;
	while(!(pSpi->dwSPISR & AT91C_SPI_SPIENS));
}


static __INIT__ int DataFlashProbe(void)
{
	struct DataFlash stTmpDataFlash;
	BYTE status;
	UINT32 ulRet;


	DRIVER_INFO("Dataflash for AT91\n");

	DataFlashSpiInit();

	DataFlashSpiEnable(0);

	ulRet = DataFlashStatus(&stTmpDataFlash.parent, &status);
	if (ulRet < 0 || status == 0xff)
	{
		DPRINT("ERROR: status error %d\n", status);
		return -ENODEV;
	}

	switch (status & 0x3c)
	{
	case 0x0c:	
		ulRet = DataFlashAdd("AT45DB011B", 512, 264, 9);
		break;

	case 0x14:	
		ulRet = DataFlashAdd("AT45DB021B", 1025, 264, 9); // 1025 ???
		break;

	case 0x1c:	
		ulRet = DataFlashAdd("AT45DB041x", 2048, 264, 9);
		break;

	case 0x24:	
		ulRet = DataFlashAdd("AT45DB081B", 4096, 264, 9);
		break;

	case 0x2c:	
		ulRet = DataFlashAdd("AT45DB161x", 4096, 528, 10);
		break;

	case 0x34:	
		ulRet = DataFlashAdd("AT45DB321x", 8192, 528, 10);
		break;

	case 0x38:	
	case 0x3c:
		ulRet = DataFlashAdd("AT45DB642x", 8192, 1056, 11);
		break;

	default:
		DPRINT("ERROR: unsupported device (%x)\n", ulRet & 0x3c);
		ulRet = -ENODEV;
	}

	if (ulRet < 0)
		DPRINT("ERROR: DataFlashAdd --> %d\n", ulRet);

	return ulRet;
}


DRIVER_INIT(DataFlashProbe);
